#include "global.h"

#define BUFFER_SIZE				80
#define NUM_SERIAL_PORTS		2

/* offset from serial ports base */
#define DATA         0   /* Data reg for Rx, Tx   */
#define DIVL         0   /* When used as divisor  */
#define DIVH         1   /* to generate baud rate */
#define IER          1   /* Interrupt Enable reg  */
#define IIR          2   /* Interrupt ID rer      */
#define LCR          3   /* Line Control reg      */
#define MCR          4   /* Modem Control reg     */
#define LSR          5   /* Line Status reg       */
#define MSR          6   /* Modem Status reg      */

typedef struct serialPort
{
	// Input Buffer
	char inputBuf[BUFFER_SIZE];
	int inputHead, inputTail;
	Semaphore inputSemaphore;
	
	// Output Buffer
	char outputBuf[BUFFER_SIZE];
	int outputHead, outputTail;
	Semaphore outputSemaphore;
	int tx_on;
	
	// Echo Buffer
	char echoBuf[BUFFER_SIZE];
	int echoHead, echoTail, echoCount;
	
	// Control
	char echo;
	char is_on;
	char erase, kill, intr, quit, x_on, x_off, eof;
	
	// This serial ports i/o address
	int port;
	
} SerialPort;

SerialPort stty[NUM_SERIAL_PORTS];

void sputc(int port, char c)
{
	while(read_port(port + LSR) & 0x20 == 0);
	write_port(port + DATA, c);
}

void sprints(int port, char* c)
{
	while (c != 0)
	{
		sputc(port, *(c++));
	}
}

char sgetc(int port)
{
	while(read_port(port + LSR) & 0x01 == 0);
	return read_port(port + DATA) & 0x7F;
}

void enable_tx(SerialPort* port)
{
	write_port(port->port + IER, 0x03);
	port->tx_on = 1;
}

void disable_tx(SerialPort* port)
{
	write_port(port->port + IER, 0x01);
	port->tx_on = 0;
}

void modem()
{
	printf("No modem\n");
}

void do_tx(SerialPort* port)
{
}

void do_rx(SerialPort* port)
{
}

void errors(int port)
{
	printf("Serial port error occured on port %d\n", port);
}

void serialPortHandler(int portNum)
{
	SerialPort* port = &stty[portNum];
	int interruptID, lineStatus, modemStatus, interruptType;
	
	interruptID = read_port(port->port + IIR);
	lineStatus = read_port(port->port + LSR);
	modemStatus = read_port(port->port + MSR);
	interruptType = interruptID & 7;
	
	switch (interruptType)
	{
		case 0: { modem();      } break;
		case 2: { do_rx(port); 	} break;
		case 4: { do_tx(port); 	} break;
		case 6: { errors(port); } break;
	}
	
	write_port(0x20, 0x20);
}

void s0Handler() { serialPortHandler(0); }
void s1Handler() { serialPortHandler(1); }

void initializeSerialPorts()
{
	int i;
	
	// Install the interrupt handlers
	putWord((int)s0Handler, 0x0, 48);
	putWord(0x1000, 0x0, 50);
	putWord((int)s1Handler, 0x0, 44);
	putWord(0x1000, 0x0, 46);
	
	for (i = 0; i < NUM_SERIAL_PORTS; ++i)
	{
		SerialPort* port = &stty[i];
		
		printf("  * Initializing Serial Port %d... ", i);
		
		port->port = (i == 0) ? 0x3F8 : 0x2F8;
		port->inputSemaphore.value = 0;
		port->inputSemaphore.queue = 0;
		port->outputSemaphore.value = BUFFER_SIZE;
		port->outputSemaphore.queue = 0;
		port->inputHead = port->outputHead = port->echoHead = 0;
		port->inputTail = port->outputTail = port->echoTail = 0;
		port->echoCount = 0;
		
		mem_lock();
		
		write_port(port->port + MCR, 0x09);
		write_port(port->port + IER, 0x00);
		write_port(port->port + LCR, 0x80);
		write_port(port->port + DIVH, 0x00);
		write_port(port->port + DIVL, 12);
		write_port(port->port + LCR, 0x03);
		write_port(port->port + MCR, 0x0B);
		write_port(port->port + IER, 0x01);
		
		mem_unlock();
		
		enable_irq(4 - i);
		
		sprints(port->port, "Serial Port Ready\r\n");
		printf("Done!\n");
	}
}
